package br.ufpb.di.ppgi.negocio.servicos;

import java.util.ArrayList;
import java.util.List;

import br.com.caelum.vraptor.ioc.Component;
import br.ufpb.di.ppgi.negocio.iservicos.IDefesaService;
import br.ufpb.di.ppgi.negocio.modelo.convidado.Convidado;
import br.ufpb.di.ppgi.negocio.modelo.defesa.Defesa;
import br.ufpb.di.ppgi.negocio.modelo.defesa.DefesaStatus;
import br.ufpb.di.ppgi.negocio.modelo.homologacao.defesa.HomologacaoDefesa;
import br.ufpb.di.ppgi.negocio.modelo.homologacao.defesa.ResultadoHomologacaoDefesa;
import br.ufpb.di.ppgi.negocio.modelo.participa.ParticipaDefesa;
import br.ufpb.di.ppgi.negocio.modelo.professorExterno.ProfessorExterno;
import br.ufpb.di.ppgi.negocio.modelo.proposta.Proposta;
import br.ufpb.di.ppgi.negocio.modelo.usuario.Usuario;
import br.ufpb.di.ppgi.negocio.modelo.usuario.UsuarioRole;
import br.ufpb.di.ppgi.persistencia.QueryLocator;
import br.ufpb.di.ppgi.persistencia.domainstore.DomainStore;
import br.ufpb.di.ppgi.persistencia.pagination.ListaPaginada;
import br.ufpb.di.ppgi.util.logger.Logger;
import br.ufpb.di.ppgi.util.logger.LoggerFactory;

@Component
public class DefesaService implements IDefesaService {
	
	private static final Logger LOGGER = LoggerFactory.getLogger(DefesaService.class);
		
	private final DomainStore ds;
	private final QueryLocator queries;
	
	public DefesaService(DomainStore ds, QueryLocator queries) {
		this.ds = ds;
		this.queries = queries;
	}

	
	private List<Usuario> getColegiadosEmVigencia() {
		List<UsuarioRole> roles = new ArrayList<UsuarioRole>();
		roles.add(UsuarioRole.COLEGIADO);
		roles.add(UsuarioRole.COORDENADOR);
		
		return queries.forUsuario().listByRoles(roles);
	}
	
	@Override
	public void adicionar(Defesa defesa) {
		List<Usuario> colegiados = getColegiadosEmVigencia();
		List<Usuario> professores = defesa.getProfessores();
		List<ProfessorExterno> profExternos = defesa.getProfExternos();
		
		defesa.setColegiados(colegiados);
		ds.add(defesa);
		
		for (Usuario usuario : colegiados) {
			ds.add(new HomologacaoDefesa(defesa, usuario));
		}
		for (Usuario usuario : professores) {
			ds.add(new ParticipaDefesa(defesa, usuario));
		}
		for (ProfessorExterno professorExterno : profExternos) {
			ds.add(new Convidado(professorExterno, defesa));
		}
	}


	@Override
	public Defesa apagar(Long id) {
		LOGGER.debug("Metodo nao implementado.");
		return null;
	}

	@Override
	public Defesa atualizar(Defesa defesa) {
		return ds.merge(defesa);
	}

	@Override
	public Defesa buscar(Long id) {
		Defesa defesa = ds.get(Defesa.class, id);
		
		List<Usuario> professores = queries.forDefesa().listProfessores(defesa);
		defesa.setProfessores(professores);
		
		List<ProfessorExterno> profExternos = queries.forDefesa().listProfExternos(defesa);
		defesa.setProfExternos(profExternos);
		
		return defesa;
	}

	@Override
	public Defesa copiarDados(Proposta proposta) {
		Defesa defesa = new Defesa();
		defesa.setAluno(proposta.getAluno());
		defesa.setColegiados(proposta.getColegiados());
		defesa.setOrientador(proposta.getOrientador());
		defesa.setProfessor1(proposta.getProfessor1());
		defesa.setProfessor2(proposta.getProfessor2());
		defesa.setTitulo(proposta.getTitulo());
		defesa.setResumo(proposta.getResumo());
		return defesa;
	}

	@Override
	public List<Defesa> listar() {
		return ds.list(Defesa.class);
	}

	@Override
	public List<Defesa> listarByUsuarioId(Long id) {
		return queries.forDefesa().listByOrientadorId(id);
	}


	@Override
	public List<Defesa> listarPendencias(Usuario usuario) {
		return queries.forDefesa().listPendenciasByUsuario(usuario);
	}


	@Override
	public boolean homologar(HomologacaoDefesa homologacao, boolean homologado) {
		homologacao = ds.get(HomologacaoDefesa.class, homologacao.getPk());
		homologacao.setHomologado(homologado);
		
		homologacao = ds.merge(homologacao);

		Defesa defesa = homologacao.getPk().getDefesa();
		ResultadoHomologacaoDefesa resultadoHomologacao = calcularResultado(defesa);
		
		if (resultadoHomologacao.isEncerrado()) {
			defesa = ds.get(Defesa.class, defesa.getId());
			defesa.setStatus(resultadoHomologacao.getResultado());
			defesa = ds.merge(defesa);
			fechaHomologacao(defesa);
		}
		
		return homologacao.isHomologado();
	}

	private void fechaHomologacao(Defesa defesa) {
		List<HomologacaoDefesa> homologacoes = queries.forDefesa().listPendencias(defesa);
		for(HomologacaoDefesa homologacao : homologacoes) {
			homologacao.setHomologado(false);
			ds.merge(homologacao);
		}
	}

	private ResultadoHomologacaoDefesa calcularResultado(Defesa defesa) {
		int metadeDosVotos = queries.forDefesa().countTotalVotos(defesa)/2;
		
		if (queries.forDefesa().isVotacaoInsuficiente(defesa)) 
			;
		else if (queries.forDefesa().countVotosHomologacao(defesa,true) >= metadeDosVotos)
			return new ResultadoHomologacaoDefesa(true,DefesaStatus.HOMOLOGADA);
		else if (queries.forDefesa().countVotosHomologacao(defesa,false) >= metadeDosVotos)
			return new ResultadoHomologacaoDefesa(true,DefesaStatus.HOMOLOGACAO_REPROVADA);
		
		return new ResultadoHomologacaoDefesa(false,DefesaStatus.PENDENTE);
	}


	@Override
	public List<Defesa> listarByStatus(DefesaStatus pendencias) {
		return queries.forDefesa().listByStatus(pendencias);
	}


	@Override
	public Boolean aprovar(Defesa defesa, Boolean aprovado) throws Exception {
		defesa = ds.get(Defesa.class, defesa.getId());
		
		if (defesa.getStatus() != DefesaStatus.HOMOLOGADA)
			throw new Exception("Não é possivel aprovar uma Defesa não homologada");
		
		if (aprovado)
			defesa.setStatus(DefesaStatus.APROVADA);
		else
			defesa.setStatus(DefesaStatus.REPROVADA);
		
		defesa = ds.merge(defesa);

		return aprovado;
	}


	@Override
	public ListaPaginada<Defesa> filtrarPaginando(String keyword, Integer page,
			String base) {
		
		List<DefesaStatus> statusAceitaveisDefesa = DefesaStatus.asList();
		
		return queries.forDefesa().paginarByKeyAndStatusList(keyword,statusAceitaveisDefesa)
						.query(1, page)
						.setBase(base)
						.toVersaoEmPortugues();
	}

}
